package com.zdsx.config;

import java.util.Map;

public class JsonStringBuilder {
    private static final char OBJ_START = '{';
    private static final char OBJ_END = '}';
    private static final char FIELD_SPLIT = ',';
    private static final char VALUE_SPLIT = ':';
    private static final String NULL_VALUE = "null";
    public static final char WRAP = '"';
    private static final char ESCAPE_PREFIX = '\\';

    private final static int[] OUTPUT_ESCAPES;

    static {
        int[] table = new int[128];
        for (int i = 0; i < 32; ++i) {
            table[i] = -1;
        }
        table['"'] = '"';
        table['\\'] = '\\';
        table[0x08] = 'b';
        table[0x09] = 't';
        table[0x0C] = 'f';
        table[0x0A] = 'n';
        table[0x0D] = 'r';
        OUTPUT_ESCAPES = table;
    }

    private final StringBuilder stringBuilder;

    public JsonStringBuilder() {
        this(512);
    }

    public JsonStringBuilder(int capacity) {
        this.stringBuilder = new StringBuilder(capacity);
    }

    public JsonStringBuilder startObject() {
        this.stringBuilder.append(OBJ_START);
        return this;
    }

    public JsonStringBuilder endObject() {
        this._removeSplit();
        this.stringBuilder.append(OBJ_END);
        this.stringBuilder.append(FIELD_SPLIT);
        return this;
    }

    public JsonStringBuilder startObject(String name) {
        return _field(name).startObject();
    }

    public JsonStringBuilder field(String name, String value) {
        if (value == null) {
            return nullField(name);
        }
        _field(name);
        this.stringBuilder.append(WRAP);
        for (char c : value.toCharArray()) {
            if (c <= 0x7F && OUTPUT_ESCAPES[c] != 0) {
                this.stringBuilder.append(ESCAPE_PREFIX);
                this.stringBuilder.append(c);
            } else {
                this.stringBuilder.append(c);
            }
        }
        this.stringBuilder.append(WRAP);
        this.stringBuilder.append(FIELD_SPLIT);
        return this;
    }

    public JsonStringBuilder field(String name, Integer value) {
        if (value == null) {
            return nullField(name);
        }
        return fieldRawValue(name, Integer.toString(value));
    }

    public JsonStringBuilder fieldRawValue(String name, String value) {
        if (value == null) {
            return nullField(name);
        }
        _field(name);
        this.stringBuilder.append(value);
        this.stringBuilder.append(FIELD_SPLIT);
        return this;
    }

    public JsonStringBuilder field(String name, Map<String, String> value) {
        if (value == null) {
            return nullField(name);
        }
        startObject(name);
        for (Map.Entry<String, String> entry : value.entrySet()) {
            field(entry.getKey(), entry.getValue());
        }
        endObject();
        return this;
    }


    public JsonStringBuilder nullField(String name) {
        return fieldRawValue(name, NULL_VALUE);
    }

    public String build() {
        this._removeSplit();
        return this.stringBuilder.toString();
    }

    private void _removeSplit() {
        int last = this.stringBuilder.length() - 1;
        if (this.stringBuilder.charAt(last) == FIELD_SPLIT) {
            this.stringBuilder.deleteCharAt(last);
        }
    }

    private JsonStringBuilder _field(String name) {
        this.stringBuilder.append(WRAP);
        this.stringBuilder.append(name);
        this.stringBuilder.append(WRAP);
        this.stringBuilder.append(VALUE_SPLIT);
        return this;
    }


    public static byte[] compress(byte[] data) {
        int offset = 0;
        int length = data.length;
        final byte[] output = new byte[length];
        int counter = 0;
        for (int index = 0; index < length; index++) {
            byte b = data[index];
            if (b == 0x08) {
                continue;
            } else if (b == 0x09) {
                continue;
            } else if (b == 0x0C) {
                continue;
            } else if (b == 0x0A) {
                continue;
            } else if (b == 0x0D) {
                continue;
            } else {
                if (b == 0x22) {
                    counter++;
                }
                if (counter % 2 == 0) {
                    if (b == 0x20) {
                        continue;
                    }
                }
                output[offset] = b;
                offset++;
            }
        }
        if (offset == length) {
            return output;
        } else {
            byte[] dest = new byte[offset];
            System.arraycopy(output, 0, dest, 0, offset);
            return dest;
        }
    }

    public static void escape(String s, StringBuffer sb) {
        if (s == null) return;
        final int len = s.length();
        for (int i = 0; i < len; i++) {
            char ch = s.charAt(i);
            switch (ch) {
                case '"':
                    sb.append("\\\"");
                    break;
                case '\\':
                    sb.append("\\\\");
                    break;
                case '\b':
                    sb.append("\\b");
                    break;
                case '\f':
                    sb.append("\\f");
                    break;
                case '\n':
                    sb.append("\\n");
                    break;
                case '\r':
                    sb.append("\\r");
                    break;
                case '\t':
                    sb.append("\\t");
                    break;
                case '/':
                    sb.append("\\/");
                    break;
                default:
                    //Reference: http://www.unicode.org/versions/Unicode5.1.0/
                    if ((ch >= '\u0000' && ch <= '\u001F') || (ch >= '\u007F' && ch <= '\u009F') || (ch >= '\u2000' && ch <= '\u20FF')) {
                        String ss = Integer.toHexString(ch);
                        sb.append("\\u");
                        for (int k = 0; k < 4 - ss.length(); k++) {
                            sb.append('0');
                        }
                        sb.append(ss.toUpperCase());
                    } else {
                        sb.append(ch);
                    }
            }
        }//for
    }
}

